package edu.scau.pos.service;

import cn.hutool.core.util.IdUtil;
import com.alipay.api.AlipayApiException;
import com.fasterxml.jackson.core.JsonProcessingException;
import edu.scau.core.Enum.OrderStatus;
import edu.scau.core.Enum.SaleLineItemStatus;
import edu.scau.core.Enum.SaleStatus;
import edu.scau.core.exection.BusinessProcessException;
import edu.scau.core.util.RedisCache;
import edu.scau.payment.domain.Payment;
import edu.scau.payment.service.PaymentService;
import edu.scau.pos.domain.Orders;
import edu.scau.pos.domain.Product;
import edu.scau.pos.domain.Sale;
import edu.scau.pos.domain.SaleLineItem;
import edu.scau.pos.vo.ProductVo;
import edu.scau.pos.vo.SaleLineItemVo;
import edu.scau.pos.vo.SaleVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
public class SaleServiceImpl implements SaleService {
    @Autowired
    private RedisCache redisCache;
    @Autowired
    private ProductService productService;
    @Autowired
    private PaymentService paymentService;
    @Autowired
    private OrdersService ordersService;

    @Override
    public String startNewSale() {
        String saleId = "sale-" + IdUtil.getSnowflakeNextId();
        Sale currentSale = new Sale();
        Date beginTime = new Date();
        currentSale.setBeginTime(beginTime);
        currentSale.setStatus(SaleStatus.created.getSignal());
        currentSale.setSaleId(saleId);
        currentSale.setStatus(SaleStatus.created.getSignal());
        redisCache.setCacheObject(saleId, currentSale);
        return saleId;
    }

    @Override
    public int getProductStock(String productSn)  throws BusinessProcessException{
        ProductVo productVo = productService.findProductBySn(productSn);
        if (productVo == null) {
            throw new BusinessProcessException(("商品信息不存在！"));
        }
        int stock = productVo.getSkuVo() != null ? productVo.getSkuVo().getStock() : -1;
        if (stock == -1) {
            throw new BusinessProcessException("未找到" + productSn + "对应的库存信息");
        }
        return stock;
    }

    @Override
    @Transactional
    public Map<String, Object> endSale(SaleVo saleVo)  throws BusinessProcessException{
        //处理重复加购
        checkItemRepetition(saleVo);
        //获取sale缓存
        Sale sale = this.getSaleInRedis(saleVo.getSaleId());
        //判断流程
        if (sale == null) {
            throw new BusinessProcessException("销售流程已结束");
        } else if (sale.getStatus() == SaleStatus.found.getSignal()) {
            throw new BusinessProcessException("销售流程订单已创建,不允许重复创建!");
        }
        //order 关联sale+设置订单状态
        Orders successOrders = this.generateOrders(saleVo.getSaleId());
        Orders pendingOrders;
        //无挂单状态
        if (!this.judgePending(saleVo)) {
            //遍历生成SaleItem 关联order
            List<SaleLineItem> saleLineItems = this.associateWithOrder(this.generateSaleLineItemsList(saleVo.getSuccessList()), successOrders.getOrderId());
            successOrders.setSaleLineItems(saleLineItems);
            //计算订单总金额
            this.calculateInOrder(successOrders);
            //商品出库
            //更新order状态--->无挂单情况
            if (updateSkuBySn(saleVo.getSuccessList())) {
                successOrders.setStatus(OrderStatus.Unpaid.getSignal());
                successOrders.setSaleLineItems(successOrders.getSaleLineItems().stream().peek(saleLineItem ->saleLineItem.setStatus(SaleLineItemStatus.Dispatch.getSignal()) ).collect(Collectors.toList()));
            }

        } else {
            pendingOrders = this.generateOrders(saleVo.getSaleId());
            List<SaleLineItem> successSaleLineItems = this.associateWithOrder(this.generateSaleLineItemsList(saleVo.getSuccessList()), successOrders.getOrderId());
            successOrders.setSaleLineItems(successSaleLineItems);
            //商品出库
            //检查加购商品能否出库
            //更新order状态
            if (updateSkuBySn(saleVo.getSuccessList())) {
                successOrders.setStatus(OrderStatus.Unpaid.getSignal());
                successOrders.setSaleLineItems(successOrders.getSaleLineItems().stream().peek(saleLineItem ->saleLineItem.setStatus(SaleLineItemStatus.Dispatch.getSignal()) ).collect(Collectors.toList()));
            }
            List<SaleLineItem> pendingSaleLineItems = this.associateWithOrder(this.generateSaleLineItemsList(saleVo.getPendingList()), pendingOrders.getOrderId());
            pendingOrders.setSaleLineItems(pendingSaleLineItems);
            //不需要出库，直接更新状态
            pendingOrders.setStatus(OrderStatus.Backorder.getSignal());
            pendingOrders.setSaleLineItems(pendingOrders.getSaleLineItems().stream().peek(saleLineItem -> {saleLineItem.setStatus(SaleLineItemStatus.book.getSignal());}).collect(Collectors.toList()));
            //计算订单总金额
            this.calculateInOrder(successOrders);
            this.calculateInOrder(pendingOrders);
            //sale
            sale.setPendingOrder(pendingOrders);
            //sale-->创建中
        }
        sale.setSuccessOrder(successOrders);
        //更新sale的状态
        sale.setStatus(SaleStatus.found.getSignal());
        //覆盖redis
        redisCache.setCacheObject(sale.getSaleId(), sale);
        //处理返回数据
        return parseReturnMsg(sale);
    }

    /**
     * 支付订单
     *
     * @param saleId
     * @return 支付宝支付表单
     */

    @Override
    public String makePayment(String saleId) throws AlipayApiException, JsonProcessingException, BusinessProcessException {
        //获取sale
        Sale sale = this.getSaleInRedis(saleId);
        //判断流程
        if (sale == null) {
            throw new BusinessProcessException("销售流程已结束");
        }
        if (sale.getStatus() == SaleStatus.paid.getSignal() || sale.getStatus() == SaleStatus.completed.getSignal()) {
            throw new BusinessProcessException("订单已支付!");
        }
        //创建payment-->关联order
        sale.getSuccessOrder().setPayment(paymentService.createPayment(sale.getSuccessOrder().getOrderId(), sale.getSuccessOrder().getAmount()));
        sale.getSuccessOrder().setPaymentId(sale.getSuccessOrder().getPayment().getPaymentId());

        if (sale.getPendingOrder() != null) {
            sale.getPendingOrder().setPayment(paymentService.createPayment(sale.getPendingOrder().getOrderId(), sale.getPendingOrder().getAmount()));
            sale.getPendingOrder().setPaymentId(sale.getPendingOrder().getPayment().getPaymentId());
        }
        //覆盖
        redisCache.setCacheObject(saleId,sale);
        //调用支付sdk\计算总金额
        //返回调用表单
        String form = "";
        if (sale.getPendingOrder() != null) {
            form = paymentService.createAndPay(sale.getSuccessOrder().getPayment(), sale.getPendingOrder().getPayment());
        } else {
            form = paymentService.createAndPay(sale.getSuccessOrder().getPayment(), null);
        }
        //调用异步入库方法
//        this.asyncDbOperation(sale);
        return form;
    }


    /**
     * 查看订单支付详情
     * 完成入库操作
     *
     * @param saleId
     * @return
     */
    @Override
    @Transactional
    public Map checkOrderStatus(String saleId) throws AlipayApiException, JsonProcessingException, BusinessProcessException {
        Sale sale = redisCache.getCacheObject(saleId);
        if (sale == null) {
            throw new BusinessProcessException("销售流程已结束");
        }
        HashMap<String,Object> data=new HashMap<>();
        Map res=paymentService.queryByOrderID(sale.getSuccessOrder().getOrderId());
        if (res!=null) {
            data.put("msg","订单支付成功");
        }
        else {
            data.put("msg","订单支付失败");
            //阻止入库
            throw new BusinessProcessException("订单支付失败");
        }
        //调用异步入库方法
        this.asyncDbOperation(sale,res);
        return data;
    }

    /**
     * 异步入库\状态设置
     */
    @Transactional
    @Async
    void asyncDbOperation(Sale sale, Map resultMap) throws AlipayApiException, JsonProcessingException {
        //改变状态
        this.changeStatus(sale);
        //更新payment信息
        this.updatePaymentMsg(sale,resultMap);
        //入库
        this.asyncDropInDb(sale);
        //销毁
        this.destroySale(sale);
    }

    /**
     * 异步修改状态
     *
     * @param sale
     * @throws AlipayApiException
     * @throws JsonProcessingException
     */
    private void changeStatus(Sale sale) throws AlipayApiException, JsonProcessingException {
        //判断支付状态
            //改变order状态
            sale.getSuccessOrder().setStatus(OrderStatus.Paid.getSignal());
            //缺货预定不用处理
            //sale状态已支付
            sale.setStatus(SaleStatus.paid.getSignal());
            redisCache.setCacheObject(sale.getSaleId(), sale);
    }

    /**
     * 入库
     * payment-->saleItem-->order
     * sale不入库
     */
    @Transactional
    public void asyncDropInDb(Sale sale) {
        sale = redisCache.getCacheObject(sale.getSaleId());
        boolean flag = false;
        if (sale.getStatus() == SaleStatus.paid.getSignal()) {
            //payment
            flag = this.rollbackIfError(paymentService.insertPayment(sale.getSuccessOrder().getPayment()));
            //order+item入库
            flag = this.rollbackIfError(ordersService.insertOrders(sale.getSuccessOrder()));
            if (sale.getPendingOrder() != null) {
                flag = this.rollbackIfError(paymentService.insertPayment(sale.getPendingOrder().getPayment()));
                flag = this.rollbackIfError(ordersService.insertOrders(sale.getPendingOrder()));
            }
        }
        if (flag) {
            sale.setStatus(SaleStatus.completed.getSignal());
            redisCache.setCacheObject(sale.getSaleId(), sale);
        }
    }

    public boolean rollbackIfError(int result) {
        if (result <= 0) {
            log.info("入库失败");
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return false;
        }
        return true;
    }

    /**
     * 支付成功后更新pay信息
     */
    void updatePaymentMsg(Sale sale,Map resultMap) throws AlipayApiException, JsonProcessingException {
        sale = redisCache.getCacheObject(sale.getSaleId());
        Payment rePaymentMsg = paymentService.updatePaymentMsgAfterPay(sale.getSuccessOrder().getOrderId(),resultMap);
        Payment payment = sale.getSuccessOrder().getPayment();
        payment.setPayTime(rePaymentMsg.getPayTime());
        payment.setTradeId(rePaymentMsg.getTradeId());
        if (sale.getPendingOrder() != null) {
            Payment pendpayment = sale.getPendingOrder().getPayment();
            pendpayment.setPayTime(rePaymentMsg.getPayTime());
            pendpayment.setTradeId(rePaymentMsg.getTradeId());
        }
        redisCache.setCacheObject(sale.getSaleId(), sale);
    }

    /**
     * 解析endsale返回信息
     *
     * @param sale
     * @return
     */
    private Map<String, Object> parseReturnMsg(Sale sale) {
        Map<String, Object> data = new HashMap<>();
        Map<String, Object> successParam = new HashMap<>();
        Map<String, Object> pendingParam = new HashMap<>();
        successParam.put("orderId", sale.getSuccessOrder().getOrderId());
//        successParam.put("订单总金额", sale.getSuccessOrder().getAmount());
        successParam.put("orderAmount", sale.getSuccessOrder().getAmount());
//        successParam.put("商品总数", sale.getSuccessOrder().getQuantity());
        successParam.put("quantity", sale.getSuccessOrder().getQuantity());
//        successParam.put("订单状态", OrderStatus.getBySignal(sale.getSuccessOrder().getStatus()).getDesc());
        successParam.put("status", OrderStatus.getBySignal(sale.getSuccessOrder().getStatus()).getDesc());

        if (sale.getPendingOrder() == null) {
            pendingParam.put("无缺货预定商品", "");
        } else {
            pendingParam.put("orderId", sale.getPendingOrder().getOrderId());
            pendingParam.put("orderAmount", sale.getPendingOrder().getAmount());
            pendingParam.put("quantity", sale.getPendingOrder().getQuantity());
            pendingParam.put("status", OrderStatus.getBySignal(sale.getPendingOrder().getStatus()).getDesc());
        }
        data.put("SuccessOrder", successParam);
        data.put("PendingOrder", pendingParam);
        return data;
    }


    @Override
    public boolean judgeStock(SaleLineItemVo saleLineItemVo)  {
        //查询库存
        int stock = getProductStock(saleLineItemVo.getProductSn());
        return stock >= saleLineItemVo.getQuantity();
    }


    /**
     * 判断有无挂单
     *
     * @param saleVo
     * @return true 挂单
     */
    boolean judgePending(SaleVo saleVo)  {
        if (saleVo.getSuccessList().isEmpty()) {
            throw new BusinessProcessException("无加购商品,请选中要加购的商品！");
        }
        return !saleVo.getPendingList().isEmpty();
    }

    /**
     * congredis获取sale
     *
     * @param saleId
     * @return
     */
    Sale getSaleInRedis(String saleId)  {
        if (saleId == null) {
            throw new BusinessProcessException("销售流程ID为空！");
        }
        Sale sale = redisCache.getCacheObject(saleId);
        if (sale == null) {
            throw new BusinessProcessException("此销售流程已结束！");
        }
        return sale;
    }

    /**
     * 通过传入的商品list创建加购条目saleItem
     *
     * @param saleLineItemVoList 传入的加购商品列表
     * @return List<SaleLineItem>
     */
    List<SaleLineItem> generateSaleLineItemsList(List<SaleLineItemVo> saleLineItemVoList) {
        return saleLineItemVoList.stream().map(this::copyFromSaleLineItemVo).toList();
    }

    /**
     * 将Vo转为domin
     *
     * @param saleLineItemVo
     * @return SaleLineItem
     */
    SaleLineItem copyFromSaleLineItemVo(SaleLineItemVo saleLineItemVo)  {
        SaleLineItem saleLineItem = new SaleLineItem();
        //item对应商品
        String productSn = saleLineItemVo.getProductSn();
        saleLineItem.setProductSn(productSn);
        Product product = new Product(productService.findProductBySn(productSn));
        if (product == null || product.getSkuCode() == null || product.getSkuCode().isEmpty()) {
            throw new BusinessProcessException("加购商品不存在或加购商品不存在库存信息，请检查仓库信息！");
        }
        saleLineItem.setProduct(product);
        saleLineItem.setProductName(product.getProductName());
        //计算单条目总额
        double price = product.getPrice();
        saleLineItem.setQuantity(saleLineItemVo.getQuantity());
        //计算总额 数量*单价
        saleLineItem.setAmount(saleLineItem.getQuantity() * price);
        //设置正常加购状态
        saleLineItem.setStatus(SaleLineItemStatus.normal.getSignal());
        return saleLineItem;
    }
//
//    /**
//     * 判断条目信息是否完整
//     * @param saleLineItemVo
//     * @return
//     */
//    boolean judgeSaleLineItemVo(SaleLineItemVo saleLineItemVo){
//
//    }

    /**
     * 创建order，关联sale
     *
     * @param saleId
     * @return
     */
    Orders generateOrders(String saleId) {
        Orders orders = new Orders();
        //订单编号
        String orderId = "order-" + IdUtil.getSnowflakeNextId();
        orders.setOrderId(orderId);
        //关联 sale
        orders.setSaleId(saleId);
        orders.setStatus(OrderStatus.created.getSignal());
        return orders;
    }

    /**
     * saleLineItem关联order
     *
     * @param saleLineItemList 条目列表
     * @return saleLineItemList
     */
    List<SaleLineItem> associateWithOrder(List<SaleLineItem> saleLineItemList, String orderId) {
        return saleLineItemList.stream().peek(item -> item.setOrderId(orderId)).collect(Collectors.toList());
    }

    /**
     * 计算订单的总金额和商品总数
     *
     * @param orders
     */
    void calculateInOrder(Orders orders) {
        double sum = 0;
        int quantity = 0;
        for (SaleLineItem saleLineItem : orders.getSaleLineItems()) {
            sum += saleLineItem.getAmount();
            quantity += saleLineItem.getQuantity();
        }

        orders.setAmount(sum);
        orders.setQuantity(quantity);
    }

    /**
     * 处理订单商品出库
     *
     * @param saleLineItemVoList 发货商品列表
     * @return true 库存更新成功
     */
    @Transactional
    public boolean updateSkuBySn(List<SaleLineItemVo> saleLineItemVoList)  {
        for (SaleLineItemVo saleLineItemVo : saleLineItemVoList) {
            if (!judgeStock(saleLineItemVo)) {
                throw new BusinessProcessException("存在出库失败商品,请查看库存余量！");
            }
        }
        int res = productService.batchUpdateStock(saleLineItemVoList);
        if( res == saleLineItemVoList.size()){
            return true;
        }else {
            log.info("批量出库失败，中断出库事务，抛出异常");
            throw new RuntimeException("出库失败");
        }
    }

    /**
     * 检查重复加购商品
     *
     * @param saleVo
     * @return
     */
    public void checkItemRepetition(SaleVo saleVo) {
        saleVo.setSuccessList(this.mergeItemList(saleVo.getSuccessList()));
        saleVo.setPendingList(this.mergeItemList(saleVo.getPendingList()));
    }

    /**
     * 查看重复加购商品
     * @param saleLineItemVoList
     * @return
     */
    List<SaleLineItemVo> mergeItemList(List<SaleLineItemVo> saleLineItemVoList) {
        //用productSN标识为同一类item
        Map<String, SaleLineItemVo> mergedItemsMap = new LinkedHashMap<>();
        for (SaleLineItemVo saleLineItemVo : saleLineItemVoList) {
            if (mergedItemsMap.containsKey(saleLineItemVo.getProductSn())) {
                //合并 quantity
                SaleLineItemVo mergeSaleLineItemVo = mergedItemsMap.get(saleLineItemVo.getProductSn());
                mergeSaleLineItemVo.setQuantity(mergeSaleLineItemVo.getQuantity() + saleLineItemVo.getQuantity());
                mergedItemsMap.put(mergeSaleLineItemVo.getProductSn(), mergeSaleLineItemVo);
            } else {
                mergedItemsMap.put(saleLineItemVo.getProductSn(), saleLineItemVo);
            }
        }
        return mergedItemsMap.values().stream().toList();
    }

    void destroySale(Sale sale) {
        sale = redisCache.getCacheObject(sale.getSaleId());
        if (sale.getStatus() == SaleStatus.completed.getSignal()) {
            if (!redisCache.deleteObject(sale.getSaleId())) {
                log.error("sale删除失败");
            }
        }
        else
        log.info("sale状态为" + SaleStatus.getBySignal(sale.getStatus()).getDesc() + ", 未执行删除");
    }

}


